package com.mysher.http_server;

import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.util.Log;

import com.koushikdutta.async.BufferedDataSink;
import com.koushikdutta.async.ByteBufferList;
import com.koushikdutta.async.callback.CompletedCallback;
import com.koushikdutta.async.http.server.AsyncHttpServerResponse;

import java.nio.ByteBuffer;

/**
 * Created by Money on 2017/12/12.
 */

public class Encode {

    private MediaCodec codec;

    private int videoW;
    private int videoH;
    private int videoBitrate;
    private int videoFrameRate;

    private static final String TAG = "Encode";
    private static final String MIME = "Video/AVC";

    private BufferedDataSink sink;

    public Encode(int videoW, int videoH, int videoBitrate, int videoFrameRate) {
        this.videoW = videoW;
        this.videoH = videoH;
        this.videoBitrate = videoBitrate;
        this.videoFrameRate = videoFrameRate;

    }

    public void initMediaCodec(AsyncHttpServerResponse response) {
        if (response == null || !response.isOpen()){
            return;
        }
        sink = new BufferedDataSink(response);
        sink.setClosedCallback(new CompletedCallback() {
            @Override
            public void onCompleted(Exception ex) {
                releaseMediaCodec();
            }
        });
        try {
            codec = MediaCodec.createEncoderByType(MIME);
            MediaFormat format = MediaFormat.createVideoFormat(MIME, videoW, videoH);
            format.setInteger(MediaFormat.KEY_BIT_RATE, videoBitrate);
            format.setInteger(MediaFormat.KEY_FRAME_RATE, videoFrameRate);
            format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
                MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
            format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);

            codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            codec.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void encoderYUV420(byte[] data) {
        if (codec == null){
            return;
        }
        byte[] input = NV21toI420(data,null,1280,720);
        try {
            int inputBufferIndex = codec.dequeueInputBuffer(-1);
            if (inputBufferIndex >= 0) {
                ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferIndex);
                inputBuffer.clear();
                inputBuffer.put(input);
                codec.queueInputBuffer(inputBufferIndex, 0, input.length, System.currentTimeMillis(), 0);
            }

            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            int outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, 0);
            while (outputBufferIndex >= 0) {
                ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferIndex);
                byte[] outData = new byte[outputBuffer.remaining()];
                outputBuffer.get(outData, 0, outData.length);
                if (sink != null && sink.isOpen()){
                    sink.write(new ByteBufferList(outData));
                }
                codec.releaseOutputBuffer(outputBufferIndex, false);
                outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, 0);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void releaseMediaCodec() {
        if (codec != null) {
            codec.stop();
            codec.release();
            codec = null;
        }
        if (sink != null){
            sink.end();
            sink = null;
        }
    }

    /** NV21 (YYYYYYYYY:VUVU) 转 I420 (YYYYYYYY:UU:VV) */
    public static byte[] NV21toI420(final byte[] input, byte[] output, final int width, final int height) {
        if (output == null) {
            output = new byte[input.length];
        }
        final int size = width * height;
        final int quarter = size / 4;
        final int v0 = size + quarter;

        System.arraycopy(input, 0, output, 0, size); // Y is same

        for (int u = size, v = v0, o = size; u < v0; u++, v++, o += 2) {
            output[v] = input[o]; // For NV21, V first
            output[u] = input[o + 1]; // For NV21, U second
        }
        return output;
    }
}
